package com.bianpai.core;

import com.bianpai.exception.FlowExecutionException;
import com.bianpai.log.FlowLogger;
import com.bianpai.rollback.DefaultRollbackStrategy;
import com.bianpai.rollback.RollbackStrategy;

import java.util.ArrayList;
import java.util.List;

/**
 * 流程引擎，负责执行流程
 */
public class FlowEngine {
    private FlowLogger logger;
    private RollbackStrategy rollbackStrategy;
    private List<FlowExecutionListener> executionListeners;
    
    public FlowEngine(FlowLogger logger) {
        this.logger = logger;
        this.rollbackStrategy = new DefaultRollbackStrategy();
        this.executionListeners = new ArrayList<>();
    }
    
    /**
     * 执行流程
     * @param flow 流程定义
     * @param context 流程上下文
     * @return 流程执行结果
     */
    public FlowResult execute(Flow flow, FlowContext context) {
        logger.logFlowStart(flow, context);
        
        try {
            // 通知监听器流程开始
            notifyFlowStarted(flow, context);
            
            // 使用流程的执行策略执行节点
            boolean success = flow.getExecutionStrategy().execute(flow, context, logger);
            
            if (success) {
                logger.logFlowComplete(flow, context, true, null);
                notifyFlowCompleted(flow, context, true);
                return FlowResult.success(context);
            } else {
                // 执行回滚
                rollbackStrategy.rollback(flow, context, logger);
                logger.logFlowComplete(flow, context, false, "Flow execution failed");
                notifyFlowCompleted(flow, context, false);
                return FlowResult.failure("Flow execution failed", context);
            }
        } catch (Exception e) {
            // 执行回滚
            rollbackStrategy.rollback(flow, context, logger);
            logger.logFlowComplete(flow, context, false, e.getMessage());
            notifyFlowError(flow, context, e);
            return FlowResult.failure("Flow execution error: " + e.getMessage(), e, context);
        }
    }
    
    /**
     * 设置回滚策略
     * @param rollbackStrategy 回滚策略
     */
    public void setRollbackStrategy(RollbackStrategy rollbackStrategy) {
        this.rollbackStrategy = rollbackStrategy;
    }
    
    /**
     * 添加流程执行监听器
     * @param listener 监听器
     */
    public void addExecutionListener(FlowExecutionListener listener) {
        executionListeners.add(listener);
    }
    
    private void notifyFlowStarted(Flow flow, FlowContext context) {
        for (FlowExecutionListener listener : executionListeners) {
            listener.onFlowStarted(flow, context);
        }
    }
    
    private void notifyFlowCompleted(Flow flow, FlowContext context, boolean success) {
        for (FlowExecutionListener listener : executionListeners) {
            listener.onFlowCompleted(flow, context, success);
        }
    }
    
    private void notifyFlowError(Flow flow, FlowContext context, Exception e) {
        for (FlowExecutionListener listener : executionListeners) {
            listener.onFlowError(flow, context, e);
        }
    }
    
    /**
     * 流程执行监听器接口
     */
    public interface FlowExecutionListener {
        void onFlowStarted(Flow flow, FlowContext context);
        void onFlowCompleted(Flow flow, FlowContext context, boolean success);
        void onFlowError(Flow flow, FlowContext context, Exception e);
    }
    
    /**
     * 流程执行结果
     */
    public static class FlowResult {
        private boolean success;
        private String message;
        private Throwable exception;
        private FlowContext context;
        
        private FlowResult(boolean success, String message, Throwable exception, FlowContext context) {
            this.success = success;
            this.message = message;
            this.exception = exception;
            this.context = context;
        }
        
        public static FlowResult success(FlowContext context) {
            return new FlowResult(true, "Flow executed successfully", null, context);
        }
        
        public static FlowResult failure(String message, FlowContext context) {
            return new FlowResult(false, message, null, context);
        }
        
        public static FlowResult failure(String message, Throwable exception, FlowContext context) {
            return new FlowResult(false, message, exception, context);
        }
        
        public boolean isSuccess() {
            return success;
        }
        
        public String getMessage() {
            return message;
        }
        
        public Throwable getException() {
            return exception;
        }
        
        public FlowContext getContext() {
            return context;
        }
    }
}